package net.llz.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import net.llz.enums.BizCodeEnum;
import net.llz.enums.CouponCategoryEnum;
import net.llz.enums.CouponPublishEnum;
import net.llz.enums.CouponStateEnum;
import net.llz.exception.BizException;
import net.llz.interceptor.LoginInterceptor;
import net.llz.mapper.CouponMapper;
import net.llz.mapper.CouponRecordMapper;
import net.llz.model.CouponDO;
import net.llz.model.CouponRecordDO;
import net.llz.model.LoginUser;
import net.llz.request.NewUserCouponRequest;
import net.llz.service.CouponService;
import net.llz.util.CommonUtil;
import net.llz.util.JsonData;
import net.llz.vo.CouponVO;
import org.redisson.RedissonLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CouponServiceImpl implements CouponService {

    @Autowired
    private CouponMapper couponMapper;

    @Autowired
    private CouponRecordMapper couponRecordMapper;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public Map<String, Object> pageCouponActivity(int page, int size) {

        Page<CouponDO> pageInfo =new Page<>(page,size);

        IPage<CouponDO> couponDOIPage =couponMapper.selectPage(pageInfo,new QueryWrapper<CouponDO>()
                .eq("publish", CouponPublishEnum.PUBLISH)
                .eq("category", CouponCategoryEnum.PROMOTION)
                .orderByDesc("create_time"));

        Map<String,Object> pageMap =  new HashMap<>(3);
        pageMap.put("total_record",couponDOIPage.getTotal());
        pageMap.put("total_page",couponDOIPage.getPages());
        pageMap.put("current_data",couponDOIPage.getRecords().stream().map(obj->beanProcess(obj)).collect(Collectors.toList()));


        return pageMap;
    }

    /**
     * 领劵接口
     * 1、获取优惠券是否存在
     * 2、校验优惠券是否可以领取：时间、库存、超过限制
     * 3、扣减库存
     * 4、保存领劵记录
     * @param couponId
     * @param category
     * @return
     */

    @Override
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public JsonData addCoupon(long couponId, CouponCategoryEnum category)  {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        String lockKey = "lock:coupont"+couponId;

        RLock rLock =redissonClient.getLock(lockKey);
        //多个线程进来 阻塞等待解锁成功
        rLock.lock();

        log.info("领卷接口加锁{}",Thread.currentThread().getId());
        try {


        CouponDO couponDO =couponMapper.selectOne(new QueryWrapper<CouponDO>().eq("id", couponId)
                .eq("category", category)
                .eq("publish", CouponPublishEnum.PUBLISH));

            this.couponCheck(couponDO,loginUser.getId());


        CouponRecordDO couponRecordDO = new CouponRecordDO();
        couponRecordDO.setId(null);
        couponRecordDO.setCreateTime(new Date());
        couponRecordDO.setUserId(loginUser.getId());
        couponRecordDO.setUserName(loginUser.getName());
        couponRecordDO.setCouponId(couponId);
        couponRecordDO.setUseState(CouponStateEnum.NEW.name());

        //高并发下扣减劵库存，采用乐观锁,当前stock做版本号,延伸多种防止超卖的问题,一次只能领取1张，TODO
        int rows = couponMapper.reduceStock(couponId);

            if(rows == 1){
                //库存扣减成功才保存
                couponRecordMapper.insert(couponRecordDO);
            }else {
                log.warn("发放优惠券失败:{},用户:{}",couponDO,loginUser);
                throw new BizException(BizCodeEnum.COUPON_GET_FAIL);
            }

        } catch (BizException e) {
            throw new RuntimeException(e);
        }finally {
            rLock.unlock();
            log.info("解锁成功");
        }

        return JsonData.buildSuccess();
    }

    @Override
    public JsonData initNewUserCoupon(NewUserCouponRequest newUserCouponRequest) {

        LoginUser loginUser = new LoginUser();
        loginUser.setId(newUserCouponRequest.getUserId());
        loginUser.setName(newUserCouponRequest.getName());
        LoginInterceptor.threadLocal.set(loginUser);

        List<CouponDO> list = couponMapper.selectList(new QueryWrapper<CouponDO>().eq("category",CouponCategoryEnum.NEW_USER));

        for (CouponDO couponDO : list){
            this.addCoupon(couponDO.getId(),CouponCategoryEnum.NEW_USER);
        }

        return JsonData.buildSuccess();
    }

    private void couponCheck(CouponDO couponDO, Long userId) throws BizException {
        //优惠券不存在
        if(couponDO == null){
            throw  new BizException(BizCodeEnum.COUPON_NO_EXITS);
        }
        //库存不足
        if(couponDO.getStock()<=0){
            throw  new BizException(BizCodeEnum.COUPON_NO_STOCK);
        }
        //是否在领取时间范围
        long time = CommonUtil.getCurrentTimestamp();
        long start = couponDO.getStartTime().getTime();
        long end = couponDO.getEndTime().getTime();
        if(time < start || time > end){
            throw  new BizException(BizCodeEnum.COUPON_OUT_OF_TIME);
        }
        //用户是否超过限制
        int recordNum = couponRecordMapper.selectCount(new QueryWrapper<CouponRecordDO>()
                .eq("coupon_id",couponDO.getId())
                .eq("user_id",userId));

        if(recordNum>=couponDO.getUserLimit()){
            throw  new BizException(BizCodeEnum.COUPON_OUT_OF_LIMIT);
        }
    }

    private CouponVO beanProcess(CouponDO couponDO) {
        CouponVO couponVO = new CouponVO();
        BeanUtils.copyProperties(couponDO,couponVO);

        return couponVO;
    }
}
